/* ObjectRenderable.h
 * Defines the basic renderable characteristics of sprites and animated objects
 *
 * Last Modified: 08.12.2013
 * Author: Guilherme S.G.
 */

#ifndef __JB_OBJECT_RENDERABLE_H__
#define __JB_OBJECT_RENDERABLE_H__

#include "Global.h"

typedef enum {STILL, ENDED, ROLLING} ANIMATION_STATE;

typedef struct Coordinate
{
    int x;
    int y;
    
    Coordinate(int x_, int y_)
    {
        x = x_;
        y = y_;
    }
    
}Coordinate;

typedef struct Frame
{
    int x;
    int y;
    int w;
    int h;
    int id;
    
    Frame(int id_, int x_, int y_, int w_, int h_)
    {
        id = id_;
        x = x_;
        y = y_;
        w = w_;
        h = h_;
    }
}Frame;

class ObjectRenderable
{
protected:
    unsigned int width;
    unsigned int height;
    double positionX;
    double positionY;
    ALLEGRO_BITMAP *image;
    
public:
    virtual ~ObjectRenderable() {}
    
    virtual void loadImage(std::string filename)
    {
        image = al_load_bitmap(filename.c_str());
    }
    
    virtual void setImage(ALLEGRO_BITMAP *image_)
    {
        image = image_;
    }
    
    virtual void setSize(int width_, int height_)
    {
        width = width_;
        height = height_;
    }
    
    virtual void setPosition(double positionX_, double positionY_)
    {
        positionX = positionX_;
        positionY = positionY_;
    }
    
    virtual int getWidth()
    {
        return width;
    }
    
    virtual int getHeight()
    {
        return height;
    }
    
    virtual void render(Frame *region, int flags)
    {
        /* Draw the whole image */
        if(region == nullptr)
        {
            al_draw_bitmap(image, positionX, positionY, flags);
        }
        else /* Draw a region from the image */
        {
            al_draw_bitmap_region(image, region->x, region->y, region->w, region->h, positionX, positionY, flags);
        }
    }
    
    /* Make the class abstract
     * Forces every child to implement an update method */
    virtual void update() = 0;
};

class Sprite : public ObjectRenderable
{
private:
    std::string name;
    std::vector<Frame> frames;
    int currentFrame;
    int frameDelay;
    int animationDirection;
    int cycles;
    int start, end;
    int flag;
    int count;
    int drawingFlag;
    
public:
    Sprite(std::string name_)
    {
        name = name_;
        currentFrame = 0;
        frameDelay = 5;
        animationDirection = 1;
        start = end = 0;
        cycles = 0;
        flag = STILL;
        count = -1;
        drawingFlag = 0;
    }
    
    virtual ~Sprite() {}
    
    void setParameters(int frameDelay_, int animationDirection_, int start_, int end_, int cycles_)
    {
        frameDelay = frameDelay_;
        animationDirection = animationDirection_;
        
        start = start_;
        end = end_;
        cycles = cycles_;
    }
    
    void addFrame(Frame newFrame)
    {
        frames.push_back(newFrame);
        end = (int)frames.size()-1;
    }
    
    void setFlag(ANIMATION_STATE state_)
    {
        flag = state_;
    }
    
    void setAnimationFlag(int flag_)
    {
        drawingFlag = flag_;
    }
    
    int getFlag()
    {
        return flag;
    }
    
    Frame getFrame(int id_)
    {
        return frames[id_];
    }
    
    void update()
    {
        setSize(frames[currentFrame].w, frames[currentFrame].h);
        
        count += animationDirection;
        currentFrame = count/frameDelay;
        
        if(currentFrame > end)
        {
            if(cycles > 0) //reset
            {
                currentFrame = start;
                count = -1;
                cycles--;
            }
            else if(cycles == 0)
            {
                flag = ENDED;
                return;
            }
            else if(cycles == -2) //ping-pong effect
            {
                currentFrame = end-1;
                animationDirection = -1;
                count -= currentFrame*frameDelay;
            }
            else //-1 infinit loop
            {
                currentFrame = start;
                count = -1;
            }
        }
        else if(currentFrame < start && cycles == -2)
        {
            currentFrame = start+1;
            animationDirection = 1;
            count = currentFrame*frameDelay;
        }
    }
    
    void animate()
    {
        al_draw_bitmap_region(image, frames[currentFrame].x, frames[currentFrame].y, frames[currentFrame].w, frames[currentFrame].h, positionX, positionY, drawingFlag);
    }
    
    void clip(int nrow_, int ncol_, int frameWidth_, int frameHeight_)
    {
        int id = 0;
        
        for(int i = 0; i < nrow_; i++)
        {
            for(int j = 0; j < ncol_; j++)
            {
                Frame newFrame = Frame(id++, j*frameWidth_, i*frameHeight_, frameWidth_, frameHeight_);
                
                frames.push_back(newFrame);
            }
        }
    }
    
    void clip(int startX, int startY, int nrow_, int ncol_, int frameWidth_, int frameHeight_)
    {
        int id = 0;
        
        for(int i = 0; i < nrow_; i++)
        {
            for(int j = 0; j < ncol_; j++)
            {
                Frame newFrame = Frame(id++, startX + j*frameWidth_, startY + i*frameHeight_, frameWidth_, frameHeight_);
                
                frames.push_back(newFrame);
            }
        }
    }
    
    std::string getName()
    {
        return name;
    }
    
    void printParam()
    {
        printf("%d %d %d %d f:%d c:%d\n", start, end, frameDelay, cycles, flag, currentFrame);
    }
};

#endif
